#!/usr/bin/env python
# -*- coding: utf-8 -*-
from UserDict import UserDict

class odict(UserDict):
    def __init__(self, dict = None):
        self._keys = []
        UserDict.__init__(self, dict)
    
    def __setitem__(self, key, item):
        UserDict.__setitem__(self, key, item)
        if key not in self._keys: self._keys.append(key)

    def copy(self):
        dict = UserDict.copy(self)
        dict._keys = self._keys[:]
        return dict

    def items(self):
        return zip(self._keys, self.values())
    
    def keys(self):
        return self._keys

    def update(self, dict):
        UserDict.update(self, dict)
        for key in dict.keys():
            if key not in self._keys: self._keys.append(key)
    
    def setdefault(self, key, failobj = None):
        UserDict.setdefault(self, key, failobj)
        if key not in self._keys: self._keys.append(key)

    def values(self):
        return map(self.get, self._keys)

class Storage:
    def __init__(self):
        self.currfun = []
        self.stack   = odict()
        self.funcs   = {}

    def clear(self):
        self.currfun = []
        self.stack  = odict()

    def add(self, state):
        self.stack[self.currfun[-1]].append(state)

    def flush(self):
        self.stack.reverse();
        for func in self.stack:
            for state in func:
                self.states.append(state)
        self.stack = odict()

    def call_fun(self, fn, *args):
        key = '%s%s'%(getattr(fn,'__name__'),args)
        if self.stack.has_key(key):
            return key
        
        self.currfun.append(key)
        self.stack[key] = []
        fn(*args)
        self.currfun.pop()
        
        return key

    def output(self):
        states = []
        labels = {}
        cnt    = 0

        for name, fun in self.stack.items():
            labels[name] = cnt
            cnt += len(fun)
        
        for name, funs in self.stack.items():
            for elem in funs:
                states.append(elem.output(labels, len(states)+1))
        return states

def state(f):
    def wraper(*args):
        return Program.call_fun(f, *args)
    setattr(wraper, '__name__', getattr(f, '__name__'))
    return wraper


Program = Storage()

def find_label(state, labels, nxt):
    if state == next:
        return nxt
    if type(state) == str:
        return labels[state]
    else:
        key = '%s()'%(getattr(state,'__name__'))
        return labels[key]

class Sense:
    def __init__(self, direction, state_ok, state_else, condition):
        self.direction  = direction
        self.state_ok   = state_ok
        self.state_else = state_else
        self.condition  = condition
        Program.add(self)

    def output(self, labels, next):
        return 'Sense %s %s %s %s'%(self.direction, find_label(self.state_ok, labels, next), find_label(self.state_else, labels, next), self.condition)

class Mark:
    def __init__(self, marker, state):
        self.marker = marker
        self.state  = state
        Program.add(self)

    def output(self, labels, next):
        return 'Mark %s %s'%(self.marker, find_label(self.state, labels, next))

class UnMark:
    def __init__(self, marker, state):
        self.state  = state
        self.marker = marker
        self.state  = state
        Program.add(self)

    def output(self, labels, next):
        return 'Unmark %s %s'%(self.marker, find_label(self.state, labels, next))

class PickUp:
    def __init__(self, state_ok, state_else):
        self.state_ok   = state_ok
        self.state_else = state_else
        Program.add(self)

    def output(self, labels, next):
        return 'PickUp %s %s'%(find_label(self.state_ok, labels, next), find_label(self.state_else, labels, next))

class Drop:
    def __init__(self, state):
        self.state = state
        Program.add(self)

    def output(self, labels, next):
        return 'Drop %s'%find_label(self.state, labels, next)

class Turn:
    def __init__(self, side, state):
        self.side = side
        self.state = state
        Program.add(self)

    def output(self, labels, next):
        return 'Turn %s %s'%(self.side, find_label(self.state, labels, next))


class Move:
    def __init__(self, state_ok, state_else):
        self.state_ok   = state_ok
        self.state_else = state_else
        Program.add(self)

    def output(self, labels, next):
        return 'Move %s %s'%(find_label(self.state_ok, labels, next), find_label(self.state_else, labels, next))

class Flip:
    def __init__(self, prob, state_ok, state_else):
        self.prob       = prob
        self.state_ok   = state_ok
        self.state_else = state_else
        Program.add(self)

    def output(self, labels, next):
        return 'Flip %s %s %s'%(self.prob, find_label(self.state_ok, labels, next), find_label(self.state_else, labels, next))

def next():
    """
    Специальная метка
    """
    pass
